/*******************************************************************************
 * Copyright (c) 2000, 2005 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package com.architexa.org.eclipse.gef.ui.parts;
 
import com.architexa.org.eclipse.draw2d.EventDispatcher;
import com.architexa.org.eclipse.draw2d.IFigure;
import com.architexa.org.eclipse.draw2d.InputEvent;
import com.architexa.org.eclipse.draw2d.SWTEventDispatcher;
import com.architexa.org.eclipse.draw2d.geometry.Point;
import com.architexa.org.eclipse.gef.AccessibleEditPart;
import com.architexa.org.eclipse.gef.EditDomain;
import com.architexa.org.eclipse.gef.EditPart;
import com.architexa.org.eclipse.gef.EditPartViewer;

import java.util.HashMap;
import java.util.Map;

import org.eclipse.swt.accessibility.ACC;
import org.eclipse.swt.accessibility.AccessibleControlEvent;
import org.eclipse.swt.accessibility.AccessibleControlListener;
import org.eclipse.swt.accessibility.AccessibleEvent;
import org.eclipse.swt.accessibility.AccessibleListener;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.TraverseEvent;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.widgets.Event;



/**
 * A special event dispatcher that will route events to the {@link EditDomain} when
 * appropriate.
 * <p>
 * IMPORTANT: This class is <EM>not</EM> intended to be used or subclassed by clients.
 * @author hudsonr
 */
public class DomainEventDispatcher
	extends SWTEventDispatcher
{

/**
 * The edit domain
 */
protected EditDomain domain;
/**
 * The viewer on which this dispatcher is created.
 */
protected EditPartViewer viewer;
private boolean editorCaptured = false;
private Cursor overrideCursor;
private Map accessibles = new HashMap();
private EditPartAccessibilityDispatcher accessibilityDispatcher;

/**
 * Extended accessibility support for editpart.
 * @author hudsonr
 */
protected class EditPartAccessibilityDispatcher
	extends AccessibilityDispatcher
{
	private AccessibleEditPart get(int childID) {
		if (childID == ACC.CHILDID_SELF || childID == ACC.CHILDID_NONE)
			if (getViewer().getContents() != null)
				return (AccessibleEditPart)getViewer().getContents()
					.getAdapter(AccessibleEditPart.class);
			else
				return null;
		return (AccessibleEditPart)accessibles.get(new Integer(childID));
	}
	
	/**
	 * @see AccessibleControlListener#getChildAtPoint(AccessibleControlEvent)
	 */
	public void getChildAtPoint(AccessibleControlEvent e) {
		org.eclipse.swt.graphics.Point p = new org.eclipse.swt.graphics.Point(e.x, e.y);
		p = getViewer().getControl().toControl(p);
		EditPart part = getViewer().findObjectAt(new Point(p.x, p.y));
		if (part == null)
			return;
		AccessibleEditPart acc = (AccessibleEditPart)
			part.getAdapter(AccessibleEditPart.class);
		if (acc != null)
			e.childID = acc.getAccessibleID();
	}
	/**
	 * @see AccessibleControlListener#getChildCount(AccessibleControlEvent)
	 */
	public void getChildCount(AccessibleControlEvent e) {
		AccessibleEditPart acc = get(e.childID);
		if (acc != null)
			acc.getChildCount(e);
	}
	
	/**
	 * @see AccessibleControlListener#getChildren(AccessibleControlEvent)
	 */
	public void getChildren(AccessibleControlEvent e) {
		AccessibleEditPart acc = get(e.childID);
		if (acc != null)
			acc.getChildren(e);
	}
	
	/**
	 * @see AccessibleControlListener#getDefaultAction(AccessibleControlEvent)
	 */
	public void getDefaultAction(AccessibleControlEvent e) {
		AccessibleEditPart acc = get(e.childID);
		if (acc != null)
			acc.getDefaultAction(e);
	}
	
	/**
	 * @see AccessibleListener#getDescription(AccessibleEvent)
	 */
	public void getDescription(AccessibleEvent e) {
		AccessibleEditPart acc = get(e.childID);
		if (acc != null)
			acc.getDescription(e);
	}
	
	/**
	 * @see AccessibleControlListener#getFocus(AccessibleControlEvent)
	 */
	public void getFocus(AccessibleControlEvent e) {
		AccessibleEditPart acc = (AccessibleEditPart)
			getViewer().getFocusEditPart().getAdapter(AccessibleEditPart.class);
		if (acc != null)
			e.childID = acc.getAccessibleID();
	}
	
	/**
	 * @see AccessibleListener#getHelp(AccessibleEvent)
	 */
	public void getHelp(AccessibleEvent e) {
		AccessibleEditPart acc = get(e.childID);
		if (acc != null)
			acc.getHelp(e);
	}
	
	/**
	 * @see AccessibleListener#getKeyboardShortcut(AccessibleEvent)
	 */
	public void getKeyboardShortcut(AccessibleEvent e) {
		AccessibleEditPart acc = get(e.childID);
		if (acc != null)
			acc.getKeyboardShortcut(e);
	}
	
	/**
	 * @see AccessibleControlListener#getLocation(AccessibleControlEvent)
	 */
	public void getLocation(AccessibleControlEvent e) {
		AccessibleEditPart acc = get(e.childID);
		if (acc != null)
			acc.getLocation(e);
	}
	
	/**
	 * @see AccessibleListener#getName(AccessibleEvent)
	 */
	public void getName(AccessibleEvent e) {
		AccessibleEditPart acc = get(e.childID);
		if (acc != null)
			acc.getName(e);
	}
	
	/**
	 * @see AccessibleControlListener#getRole(AccessibleControlEvent)
	 */
	public void getRole(AccessibleControlEvent e) {
		AccessibleEditPart acc = get(e.childID);
		if (acc != null)
			acc.getRole(e);
	}
	
	/**
	 * @see AccessibleControlListener#getSelection(AccessibleControlEvent)
	 */
	public void getSelection(AccessibleControlEvent e) { }
	
	/**
	 * @see AccessibleControlListener#getState(AccessibleControlEvent)
	 */
	public void getState(AccessibleControlEvent e) {
		AccessibleEditPart acc = get(e.childID);
		if (acc != null)
			acc.getState(e);
	}
	
	/**
	 * @see AccessibleControlListener#getValue(AccessibleControlEvent)
	 */
	public void getValue(AccessibleControlEvent e) {
		AccessibleEditPart acc = get(e.childID);
		if (acc != null)
			acc.getValue(e);
	}
}

/**
 * Constructs the dispatcher for the given domain and viewer.
 * @param d the domain
 * @param v the viewer
 */
public DomainEventDispatcher(EditDomain d, EditPartViewer v) {
	domain = d;
	viewer = v;
	setEnableKeyTraversal(false);
}

/**
 * @see EventDispatcher#dispatchFocusGained(org.eclipse.swt.events.FocusEvent)
 */
public void dispatchFocusGained(FocusEvent event) {
	super.dispatchFocusGained(event);
	domain.focusGained(event, viewer);
}

/**
 * @see EventDispatcher#dispatchFocusLost(org.eclipse.swt.events.FocusEvent)
 */
public void dispatchFocusLost(FocusEvent event) {
	super.dispatchFocusLost(event);
	domain.focusLost(event, viewer);
	setRouteEventsToEditor(false);
}

/**
 * @see EventDispatcher#dispatchKeyPressed(org.eclipse.swt.events.KeyEvent)
 */
public void dispatchKeyPressed(org.eclipse.swt.events.KeyEvent e) { 
	if (!editorCaptured) {
		super.dispatchKeyPressed(e);
		if (draw2dBusy())
			return;
	}
	if (okToDispatch())
		domain.keyDown(e, viewer);
}

/**
 * @see com.architexa.org.eclipse.draw2d.SWTEventDispatcher#dispatchKeyTraversed(org.eclipse.swt.events.TraverseEvent)
 */
public void dispatchKeyTraversed(TraverseEvent e) {
	if (!editorCaptured) {
		super.dispatchKeyTraversed(e);
		if (!e.doit)
			return;
	}
	if (okToDispatch())
		domain.keyTraversed(e, viewer);
}

/**
 * @see EventDispatcher#dispatchKeyReleased(org.eclipse.swt.events.KeyEvent)
 */
public void dispatchKeyReleased(org.eclipse.swt.events.KeyEvent e) { 
	if (!editorCaptured) {
		super.dispatchKeyReleased(e);
		if (draw2dBusy())
			return;
	}
	if (okToDispatch()) 
		domain.keyUp(e, viewer);
}

/**
 * @see EventDispatcher#dispatchMouseDoubleClicked(org.eclipse.swt.events.MouseEvent)
 */
public void dispatchMouseDoubleClicked(org.eclipse.swt.events.MouseEvent me) {
	if (!editorCaptured) {
		super.dispatchMouseDoubleClicked(me);
		if (draw2dBusy())
			return;
	}
	if (okToDispatch())
		domain.mouseDoubleClick(me, viewer);
}

/**
 * @see EventDispatcher#dispatchMouseEntered(org.eclipse.swt.events.MouseEvent)
 */
public void dispatchMouseEntered(org.eclipse.swt.events.MouseEvent me) {
	if (!editorCaptured) {
		super.dispatchMouseEntered(me);
		if (draw2dBusy())
			return;
	}
	if (okToDispatch()) {
		domain.viewerEntered(me, viewer);
	}
}

/**
 * @see EventDispatcher#dispatchMouseExited(org.eclipse.swt.events.MouseEvent)
 */
public void dispatchMouseExited(org.eclipse.swt.events.MouseEvent me) {
	if (!editorCaptured) {
		super.dispatchMouseExited(me);
		if (draw2dBusy())
			return;
	}
	if (okToDispatch()) {
		domain.viewerExited(me, viewer);
	}
}

/**
 * @see EventDispatcher#dispatchMouseHover(org.eclipse.swt.events.MouseEvent)
 */
public void dispatchMouseHover(org.eclipse.swt.events.MouseEvent me) {
	if (!editorCaptured) {
		super.dispatchMouseHover(me);
		if (draw2dBusy())
			return;
	}
	if (okToDispatch())
		domain.mouseHover(me, viewer);
}

/**
 * @see EventDispatcher#dispatchMousePressed(org.eclipse.swt.events.MouseEvent)
 */
public void dispatchMousePressed(org.eclipse.swt.events.MouseEvent me) {
	if (!editorCaptured) {
		super.dispatchMousePressed(me);
		if (draw2dBusy())
			return;
	}
	if (okToDispatch()) {
		setFocus(null);
		control.forceFocus();
		setRouteEventsToEditor(true);
		domain.mouseDown(me, viewer);
	}
}

/**
 * @see EventDispatcher#dispatchMouseMoved(org.eclipse.swt.events.MouseEvent)
 */
public void dispatchMouseMoved(org.eclipse.swt.events.MouseEvent me) {
	if (!editorCaptured) {
		super.dispatchMouseMoved(me);
		if (draw2dBusy())
			return;
	}
	if (okToDispatch()) {
		if ((me.stateMask & InputEvent.ANY_BUTTON) != 0)
			domain.mouseDrag(me, viewer);
		else
			domain.mouseMove(me, viewer);
	}
}

/**
 * @see EventDispatcher#dispatchMouseReleased(org.eclipse.swt.events.MouseEvent)
 */
public void dispatchMouseReleased(org.eclipse.swt.events.MouseEvent me) {
	if (!editorCaptured) {
		super.dispatchMouseReleased(me);
		if (draw2dBusy())
			return;
	}
	if (okToDispatch()) {
		setRouteEventsToEditor(false);
		domain.mouseUp(me, viewer);
		updateFigureUnderCursor(me);
	}
}

/**
 * Dispatches a drag finished event.
 * @param event the event
 * @param viewer the viewer on which the event occured.
 */
public void dispatchNativeDragFinished(
	DragSourceEvent event,
	AbstractEditPartViewer viewer) {
	//$TODO delete the viewer parameter from the method
	domain.nativeDragFinished(event, viewer);
}

/**
 * Dispatches a drag started event.
 * @param event the event
 * @param viewer the viewer
 */
public void dispatchNativeDragStarted(
	DragSourceEvent event,
	AbstractEditPartViewer viewer) {
	//$TODO delete the viewer parameter from the method
	setRouteEventsToEditor(false);
	domain.nativeDragStarted(event, viewer);
}

/**
 * Forwards the event to the EditDomain.
 * @see com.architexa.org.eclipse.draw2d.EventDispatcher#dispatchMouseWheelScrolled(org.eclipse.swt.widgets.Event)
 */
public void dispatchMouseWheelScrolled(Event evt) {
	if (!editorCaptured)
		super.dispatchMouseWheelScrolled(evt);
	
	if (evt.doit && okToDispatch())
		domain.mouseWheelScrolled(evt, viewer);
}

private boolean draw2dBusy() {
	if (getCurrentEvent() != null)
		if (getCurrentEvent().isConsumed())
			return true;
	if (isCaptured())
		return true;
	return false;
}

/**
 * Lazily creates and returns the accessibility dispatcher.
 * @see com.architexa.org.eclipse.draw2d.EventDispatcher#getAccessibilityDispatcher()
 */
protected AccessibilityDispatcher getAccessibilityDispatcher() {
	if (accessibilityDispatcher == null)
		accessibilityDispatcher = new EditPartAccessibilityDispatcher();
	return accessibilityDispatcher;
}

/**
 * Returns the viewer on which this dispatcher was created
 * @return the viewer for this dispatcher
 */
protected final EditPartViewer getViewer() {
	return viewer;
}

private boolean okToDispatch() {
	return domain != null;
}

void putAccessible(AccessibleEditPart acc) {
	accessibles.put(new Integer(acc.getAccessibleID()), acc);
}

void removeAccessible(AccessibleEditPart acc) {
	accessibles.remove(new Integer(acc.getAccessibleID()));
}

/**
 * @see EventDispatcher#setCapture(IFigure)
 */
protected void setCapture(IFigure figure) {
	super.setCapture(figure);
	if (figure == null) {
		releaseCapture();
		setRouteEventsToEditor(true);
	}
}

/**
 * @see SWTEventDispatcher#setCursor(Cursor)
 */
protected void setCursor(Cursor newCursor) {
	if (overrideCursor == null)
		super.setCursor(newCursor);
	else
		super.setCursor(overrideCursor);
}

/**
 * Sets whether events should go directly to the edit domain.
 * @param value <code>true</code> if all events should go directly to the edit domain
 */
public void setRouteEventsToEditor(boolean value) {
	editorCaptured = value;
}

/**
 * Sets the override cursor.
 * @param newCursor the cursor
 */
public void setOverrideCursor(Cursor newCursor) {
	if (overrideCursor == newCursor)
		return;
	overrideCursor = newCursor;
	if (overrideCursor == null)
		updateCursor();
	else
		setCursor(overrideCursor);
}

}
